#include "EItem.h"
#include "DefineCommon.h"
#include "EItemMeta.h"
#include "EItemMetaProperty.h"
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <functional>

EItemMetaPtr EItem_Fun_createMeta(EItem* itemPtr)
{
    EItem* itemSubPtr = itemPtr;
    EItemMetaPtr metaPtr = NewSmartPtr(EItemMeta)();
    metaPtr->setClassName("EItem");
    return metaPtr;
}

struct EItemData
{
    /**Item tree releated data*/
    EList<EItemPtr> m_itemListParent;
    EList<EItemPtr> m_itemListChild;

    /**Item meta class*/
    EItemMetaPtr m_itemMeta;

    /**Function for EItemMeta generation*/
    Fun_createMeta m_fun_createMeta = EItem_Fun_createMeta;

    //A pointer to attached data by outer
    intptr_t m_attached = NULL;
};

EItem::EItem()
{
    m_data = new EItemData;
}

EItem::~EItem()
{
    delete m_data;
}
    
void EItem::setName(const char* strName)
{
    m_name = strName;
}

void EItem::setName(const EString& strName)
{
    this->m_name = strName;
}

EString EItem::getName()
{
    return m_name;
}

EString EItem::getType()
{
    return m_type;
}

long EItem::getTag()
{
    return (long)this;
}

void EItem::setIfShownInUI(bool boolValue)
{
    this->m_ifShownInUI = boolValue;
}

bool EItem::getIfShownInUI()
{
    return this->m_ifShownInUI;
}

void EItem::setDescription(const char* strName)
{
    this->m_description = strName;
}

EString EItem::getDescription()
{
    return this->m_description;
}

bool EItem::addChild(EItemPtr childItem)
{
    bool result = false;
    if(childItem != nullptr)
    {
        this->m_data->m_itemListChild.push_back(childItem);
        childItem->m_data->m_itemListParent.push_back(shared_from_this());
        result = true;
    }

    return result;
}

bool EItem::insertChild(int index, EItemPtr child )
{
    if (child == nullptr) 
        return false;

// #if ENSURE_CHILD_IS_UNIQUE
    if (this->containsChild(child))
    {
        ELOG_ERROR("Item \"%s\" is already a child of Item \"%s\",ignoring call", 
                child->getName().data(), this->getName().data());
        return false;
    }
// #endif

    EItemList& _children = m_data->m_itemListChild;

    if (index >= _children.size())
    {
        index = _children.size();      // set correct index value to be passed to the "childInserted" method
        _children.push_back(child);
    }
    else
    {
        _children.insert(_children.begin()+index, child);
    }

    // register as parent of child.
    child->m_data->m_itemListParent.push_back(shared_from_this());

    // tell any subclasses that a child has been inserted so that they can update themselves.
    // childInserted(index);
    //
    return true;
}

EItemPtr EItem::getChild(int index)
{
    EItemPtr result = nullptr;
    if(index >=0 && index < m_data->m_itemListChild.size())
    {
        result = m_data->m_itemListChild[index];

    }
    return result;
}

EItemPtr EItem::getParent(int index)
{
    EItemPtr result = nullptr;
    if(index >=0 && index < m_data->m_itemListParent.size())
    {
        result = m_data->m_itemListParent[index];
    }
    return result;
}

int EItem::getChildIndex(EItemPtr childPtr)
{
    EItemList& _children = m_data->m_itemListChild;
    for (unsigned int childNum=0;childNum<_children.size();++childNum)
    {
        if (_children[childNum] == childPtr) 
        {
            return childNum;
        }
    }
    return -1; // node not found.
}

int EItem::getParentIndex(EItemPtr parentPtr)
{
    std::vector<EItemPtr>& _parents = m_data->m_itemListParent;
    for (unsigned int num=0; num<_parents.size();++num)
    {
        if (_parents[num] == parentPtr) 
        {
            return num;
        }
    }
    return -1; // node not found.
}

size_t EItem::getParentCount()
{
    return m_data->m_itemListParent.size();
}


size_t EItem::getChildCount()
{
    return m_data->m_itemListChild.size();
}

bool EItem::containsChild( const EItemPtr child )
{
    EItemList& _children = m_data->m_itemListChild;

    for (EItemList::const_iterator itr=_children.begin();
            itr!=_children.end();
            ++itr)
    {
        if (*itr == child) 
            return true;
    }
    return false;
}

bool EItem::removeChild(int index, int numChildrenToRemove)
{
    bool result = false;
    if(index >= 0 || index < m_data->m_itemListChild.size())
    {
        int endOfRemoveRange = index+numChildrenToRemove;

        //remove parent ptr in child item
        for(int i = index; i < endOfRemoveRange; i++)
        {
            EItemPtr child = m_data->m_itemListChild[i];
            child->removeParent(shared_from_this());
        }

        m_data->m_itemListChild.erase(m_data->m_itemListChild.begin()+index, m_data->m_itemListChild.begin()+endOfRemoveRange);
        result = true;
    }
    return result;
}

bool EItem::removeParent(int index, int numParentToRemove)
{
    bool result = false;
    if(index >= 0 || index < m_data->m_itemListParent.size())
    {
        int endOfRemoveRange = index+numParentToRemove;
        m_data->m_itemListParent.erase(m_data->m_itemListParent.begin()+index, m_data->m_itemListParent.begin()+endOfRemoveRange);
        result = true;
    }
    return result;
}

bool EItem::removeChild(EItemPtr childPtr)
{
    EItemList& _children = m_data->m_itemListChild;
    int pos = getChildIndex(childPtr);
    if (pos >= 0 && pos<_children.size())
        return removeChild(pos,1);
    else 
        return false;
}

bool EItem::removeParent(EItemPtr parentPtr)
{
    std::vector<EItemPtr>& _parents = m_data->m_itemListParent;
    int pos = getParentIndex(parentPtr);
    if (pos >= 0 && pos<_parents.size())
        return removeParent(pos,1);
    else 
        return false;
}

bool EItem::removeChildAll()
{
    return this->removeChild(0, this->m_data->m_itemListChild.size());
}

void EItem::setAttached(intptr_t ptr)
{
    this->m_data->m_attached = ptr;
}

intptr_t EItem::getAttached()
{
    return this->m_data->m_attached;
}

void EItem::selected()
{
}

void EItem::disselected()
{
}

EItemMetaPtr EItem::getOrCreatMeta()
{
    if(m_data->m_itemMeta == nullptr && m_data->m_fun_createMeta != nullptr)
    {
        m_data->m_itemMeta = m_data->m_fun_createMeta(this);
        // m_data->m_itemMeta = NewSmartPtr(EItemMeta)();
    }

    return m_data->m_itemMeta;
}

void EItem::setFunCreateMeta(Fun_createMeta fun)
{
    m_data->m_fun_createMeta = fun;
}

EString EItem::getPrintStr()
{
    EString str = "EItem information:\n";

    str += EString("name=")+this->m_name+"\n";
    str += EString("type=")+this->m_type+"\n";
    str += EString("child count=")+EString::number(this->m_data->m_itemListChild.size())+"\n";
    str += EString("parent count=")+EString::number(this->m_data->m_itemListParent.size())+"\n";

    return str;
}

// EString EItem::toTreeItemName(const EString& name)
// {
    // EString result;
    // if(name == "osg::Group")
    // {
        // result = "Group";
    // }
    // else if(name == "osg::Node")
    // {
        // result = "Node";
    // }
    // else if(name == "osg::MatrixTransform")
    // {
        // result = "MT";
    // }
    // else if(name == "osg::Geode")
    // {
        // result = "Geod";
    // }
    // else if(name == "osg::Geometry")
    // {
        // result = "Geom";
    // }
    // else if(name == "osgAnimation::Bone")
    // {
        // result = "Bone";
    // }
    // else
    // {
        // result = name;
    // }
    // return result;
// }

